Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / service / src / commonTest / kotlin / org / meshtastic / core / service / MeshServiceOrchestratorTest.kt

Displaying Raw • Download

core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt 174315b21f5affcb952642f1650d316c86b8a40f (174315b2) Text, 8.67 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.service

Tff7b72import T7ee787co.touchlab.kermit.Severity
Tff7b72import T7ee787dev.mokkery.MockMode
Tff7b72import T7ee787dev.mokkery.answering.returns
Tff7b72import T7ee787dev.mokkery.every
Tff7b72import T7ee787dev.mokkery.matcher.any
Tff7b72import T7ee787dev.mokkery.mock
Tff7b72import T7ee787dev.mokkery.verify
Tff7b72import T7ee787dev.mokkery.verify.VerifyMode.Companion.exactly
Tff7b72import T7ee787dev.mokkery.verifySuspend
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableSharedFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.test.UnconfinedTestDispatcher
Tff7b72import T7ee787org.meshtastic.core.common.database.DatabaseManager
Tff7b72import T7ee787org.meshtastic.core.model.Node
Tff7b72import T7ee787org.meshtastic.core.model.service.ServiceAction
Tff7b72import T7ee787org.meshtastic.core.repository.CommandSender
Tff7b72import T7ee787org.meshtastic.core.repository.MeshActionHandler
Tff7b72import T7ee787org.meshtastic.core.repository.MeshConfigHandler
Tff7b72import T7ee787org.meshtastic.core.repository.MeshMessageProcessor
Tff7b72import T7ee787org.meshtastic.core.repository.MeshRouter
Tff7b72import T7ee787org.meshtastic.core.repository.MeshServiceNotifications
Tff7b72import T7ee787org.meshtastic.core.repository.NodeManager
Tff7b72import T7ee787org.meshtastic.core.repository.NodeRepository
Tff7b72import T7ee787org.meshtastic.core.repository.RadioInterfaceService
Tff7b72import T7ee787org.meshtastic.core.repository.ServiceRepository
Tff7b72import T7ee787org.meshtastic.core.repository.TakPrefs
Tff7b72import T7ee787org.meshtastic.core.takserver.TAKMeshIntegration
Tff7b72import T7ee787org.meshtastic.core.takserver.TAKServerManager
Tff7b72import T7ee787org.meshtastic.core.takserver.fountain.CoTHandler
Tff7b72import T7ee787org.meshtastic.proto.LocalModuleConfig
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertFalse
Tff7b72import T7ee787kotlin.test.assertTrue

Tff7b72class T56d364MeshServiceOrchestratorTest Tb4b4b4{

Tff7b72private Tff7b72val Te6edf3radioInterfaceServiceTb4b4b4: Te6edf3RadioInterfaceService Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3serviceRepositoryTb4b4b4: Te6edf3ServiceRepository Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3nodeManagerTb4b4b4: Te6edf3NodeManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3nodeRepositoryTb4b4b4: Te6edf3NodeRepository Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3messageProcessorTb4b4b4: Te6edf3MeshMessageProcessor Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3commandSenderTb4b4b4: Te6edf3CommandSender Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3routerTb4b4b4: Te6edf3MeshRouter Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3actionHandlerTb4b4b4: Te6edf3MeshActionHandler Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3meshConfigHandlerTb4b4b4: Te6edf3MeshConfigHandler Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3serviceNotificationsTb4b4b4: Te6edf3MeshServiceNotifications Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3takServerManagerTb4b4b4: Te6edf3TAKServerManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3takPrefsTb4b4b4: Te6edf3TakPrefs Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3cotHandlerTb4b4b4: Te6edf3CoTHandler Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3databaseManagerTb4b4b4: Te6edf3DatabaseManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)

Tff7b72private Tff7b72val Te6edf3testDispatcher Tff7b72= Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3testScope Tff7b72= Te6edf3CoroutineScopeTb4b4b4(Te6edf3testDispatcherTb4b4b4)

T8b949e/** Stubs the shared flow dependencies used by every test and returns an orchestrator. */
Tff7b72private Tff7b72fun Td2a8ffcreateOrchestratorTb4b4b4(
Te6edf3receivedDataTb4b4b4: Te6edf3MutableSharedFlowTff7b72<Te6edf3ByteArrayTff7b72> Tff7b72= Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3connectionErrorTb4b4b4: Te6edf3MutableSharedFlowTff7b72<Tffa657StringTff7b72> Tff7b72= Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3serviceActionTb4b4b4: Te6edf3MutableSharedFlowTff7b72<Te6edf3ServiceActionTff7b72> Tff7b72= Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3takEnabledFlowTb4b4b4: Te6edf3MutableStateFlowTff7b72<Tffa657BooleanTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)Tb4b4b4,
Te6edf3takRunningFlowTb4b4b4: Te6edf3MutableStateFlowTff7b72<Tffa657BooleanTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3MeshServiceOrchestrator Tb4b4b4{
Te6edf3every Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3receivedData Tb4b4b4} Te6edf3returns Te6edf3receivedData
Te6edf3every Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3connectionError Tb4b4b4} Te6edf3returns Te6edf3connectionError
Te6edf3every Tb4b4b4{ Te6edf3serviceRepositoryTb4b4b4.Te6edf3serviceAction Tb4b4b4} Te6edf3returns Te6edf3serviceAction
Te6edf3every Tb4b4b4{ Te6edf3serviceRepositoryTb4b4b4.Te6edf3meshPacketFlow Tb4b4b4} Te6edf3returns Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3meshConfigHandlerTb4b4b4.Te6edf3moduleConfig Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Te6edf3LocalModuleConfigTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3takPrefsTb4b4b4.Te6edf3isTakServerEnabled Tb4b4b4} Te6edf3returns Te6edf3takEnabledFlow
Te6edf3every Tb4b4b4{ Te6edf3takServerManagerTb4b4b4.Te6edf3isRunning Tb4b4b4} Te6edf3returns Te6edf3takRunningFlow
Te6edf3every Tb4b4b4{ Te6edf3takServerManagerTb4b4b4.Te6edf3inboundMessages Tb4b4b4} Te6edf3returns Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeRepositoryTb4b4b4.Te6edf3nodeDBbyNum Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Te6edf3emptyMapTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3routerTb4b4b4.Te6edf3actionHandler Tb4b4b4} Te6edf3returns Te6edf3actionHandler

Tff7b72val Te6edf3takMeshIntegration Tff7b72=
Te6edf3TAKMeshIntegrationTb4b4b4(
Te6edf3takServerManager Tff7b72= Te6edf3takServerManagerTb4b4b4,
Te6edf3commandSender Tff7b72= Te6edf3commandSenderTb4b4b4,
Te6edf3nodeRepository Tff7b72= Te6edf3nodeRepositoryTb4b4b4,
Te6edf3serviceRepository Tff7b72= Te6edf3serviceRepositoryTb4b4b4,
Te6edf3meshConfigHandler Tff7b72= Te6edf3meshConfigHandlerTb4b4b4,
Te6edf3cotHandler Tff7b72= Te6edf3cotHandlerTb4b4b4,
Tb4b4b4)

Tff7b72return Te6edf3MeshServiceOrchestratorTb4b4b4(
Te6edf3radioInterfaceService Tff7b72= Te6edf3radioInterfaceServiceTb4b4b4,
Te6edf3serviceRepository Tff7b72= Te6edf3serviceRepositoryTb4b4b4,
Te6edf3nodeManager Tff7b72= Te6edf3nodeManagerTb4b4b4,
Te6edf3messageProcessor Tff7b72= Te6edf3messageProcessorTb4b4b4,
Te6edf3router Tff7b72= Te6edf3routerTb4b4b4,
Te6edf3serviceNotifications Tff7b72= Te6edf3serviceNotificationsTb4b4b4,
Te6edf3takServerManager Tff7b72= Te6edf3takServerManagerTb4b4b4,
Te6edf3takMeshIntegration Tff7b72= Te6edf3takMeshIntegrationTb4b4b4,
Te6edf3takPrefs Tff7b72= Te6edf3takPrefsTb4b4b4,
Te6edf3databaseManager Tff7b72= Te6edf3databaseManagerTb4b4b4,
Te6edf3scope Tff7b72= Te6edf3testScopeTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftestStartWiresComponentsTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Tb4b4b4)

Te6edf3assertFalseTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)

Te6edf3verify Tb4b4b4{ Te6edf3serviceNotificationsTb4b4b4.Te6edf3initChannelsTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3verify Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3loadCachedNodeDBTb4b4b4(Tb4b4b4) Tb4b4b4}

Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftestTakServerStartsAndStopsWithPreferenceTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3takEnabledFlow Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3takRunningFlow Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)

Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Te6edf3takEnabledFlow Tff7b72= Te6edf3takEnabledFlowTb4b4b4, Te6edf3takRunningFlow Tff7b72= Te6edf3takRunningFlowTb4b4b4)

Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)

T8b949e// Toggle on
Te6edf3takEnabledFlowTb4b4b4.Te6edf3value Tff7b72= Tff7b72true
Te6edf3verify Tb4b4b4{ Te6edf3takServerManagerTb4b4b4.Te6edf3startTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}

T8b949e// Update mock state to reflect it's running
Te6edf3takRunningFlowTb4b4b4.Te6edf3value Tff7b72= Tff7b72true

T8b949e// Toggle off
Te6edf3takEnabledFlowTb4b4b4.Te6edf3value Tff7b72= Tff7b72false
Te6edf3verify Tb4b4b4{ Te6edf3takServerManagerTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4) Tb4b4b4}

Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftestStartCallsSwitchActiveDatabaseTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3every Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3getDeviceAddressTb4b4b4(Tb4b4b4) Tb4b4b4} Te6edf3returns Ta5d6ff"Ta5d6fftcp:192.168.1.100Ta5d6ff"

Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Tb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3databaseManagerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.100Ta5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3verify Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3connectTb4b4b4(Tb4b4b4) Tb4b4b4}

Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftestConnectionErrorForwardedToServiceRepositoryTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3connectionError Tff7b72= Te6edf3MutableSharedFlowTff7b72<Tffa657StringTff7b72>Tb4b4b4(Te6edf3extraBufferCapacity Tff7b72= T79c0ff1Tb4b4b4)

Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Te6edf3connectionError Tff7b72= Te6edf3connectionErrorTb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)

T8b949e// Emit an error into the radio interface's connectionError flow
Te6edf3connectionErrorTb4b4b4.Te6edf3tryEmitTb4b4b4(Ta5d6ff"Ta5d6ffBLE connection lostTa5d6ff"Tb4b4b4)

Te6edf3verify Tb4b4b4{ Te6edf3serviceRepositoryTb4b4b4.Te6edf3setErrorMessageTb4b4b4(Ta5d6ff"Ta5d6ffBLE connection lostTa5d6ff"Tb4b4b4, Te6edf3SeverityTb4b4b4.Te6edf3WarnTb4b4b4) Tb4b4b4}

Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftestServiceActionDispatchedToActionHandlerTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3serviceAction Tff7b72= Te6edf3MutableSharedFlowTff7b72<Te6edf3ServiceActionTff7b72>Tb4b4b4(Te6edf3extraBufferCapacity Tff7b72= T79c0ff1Tb4b4b4)

Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Te6edf3serviceAction Tff7b72= Te6edf3serviceActionTb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3action Tff7b72= Te6edf3ServiceActionTb4b4b4.Te6edf3FavoriteTb4b4b4(Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff4T79c0ff2Tb4b4b4)Tb4b4b4)
Te6edf3serviceActionTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3actionTb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3actionHandlerTb4b4b4.Te6edf3onServiceActionTb4b4b4(Te6edf3actionTb4b4b4) Tb4b4b4}

Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftestStartIsIdempotentTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Tb4b4b4)

Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)

T8b949e// Second call should be a no-op
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)

T8b949e// Components should only be initialized once
Te6edf3verifyTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3serviceNotificationsTb4b4b4.Te6edf3initChannelsTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3verifyTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3loadCachedNodeDBTb4b4b4(Tb4b4b4) Tb4b4b4}

Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.4 - Generated in 0.11s